#include <buola/gui/x11/auto.h>

#include <buola/gui/cicon.h>
#include <buola/gui/cwindow.h>
#include <buola/gui/cpointer.h>
#include <buola/utility/cregion.h>
#include <buola/image/cimage.h>
#include <buola/gui/x11/cfixesregion.h>
#include <buola/gui/x11/cx11window.h>
#include <buola/gui/x11/cx11windowwm.h>
#include <buola/gui/x11/cx11toolkit.h>
#include <buola/gui/x11/cx11display.h>
#include <buola/gui/x11/cx11drawable.h>
#include <buola/gui/x11/cx11picture.h>
#include <buola/gui/x11/cx11dispatcher.h>
#include <buola/gui/x11/cdoublebuffer.h>
#include <buola/gui/x11/cx11exposehelper.h>
#include <buola/gui/x11/cglxexposehelper.h>
#include <buola/gui/x11/cglxcontext.h>

#include <X11/Xlib.h>
#include <X11/extensions/Xfixes.h>
#include <X11/extensions/shape.h>
#include <X11/Xatom.h>
#include <buola/gui/x11/xembed.h>
#include <buola/gui/x11/cglx.h>
#include <buola/gui/x11/icccm.h>
#include <buola/gui/cwindowmanager.h>

#pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant"

namespace buola { namespace gui { namespace x11 {

//////////////////////////// static variables //////////////////////
std::unordered_map<TID,CX11Window*> CX11Window::mAllX11Windows;

/////////////////////// constructor and destructor /////////////////

CX11Window::CX11Window(CWindow *pWindow,CX11Display *pDisplay)
    :   CPhysicalWindow(pWindow)
    ,   mDisplay(pDisplay)
    ,   mCursor(nullptr)
    ,   mInputOnly(false)
    ,   mWM(nullptr)
    ,   mParent(nullptr)
    ,   mInitialEventMask(0)
    ,   mXEmbeddee(nullptr)
    ,   mFlags(FLAG_NONE)
{
    mVisual=CopyFromParent;
    mDepth=CopyFromParent;
    mX11ID=0;
}

CX11Window::CX11Window(Window pX11ID,CX11Display *pDisplay)
    :   CPhysicalWindow(nullptr)
    ,   mDisplay(pDisplay)
    ,   mCursor(nullptr)
    ,   mInputOnly(false)
    ,   mWM(nullptr)
    ,   mParent(nullptr)
    ,   mInitialEventMask(0)
    ,   mXEmbeddee(nullptr)
    ,   mFlags(FLAG_FOREIGN)
{
    if(pX11ID==0) pX11ID=DefaultRootWindow(mDisplay->GetDisplay());

    if(pX11ID==(Window)DefaultRootWindow(mDisplay->GetDisplay()))
    {
        SetFlag(FLAG_ROOT);
    }

    mX11ID=pX11ID;

    XWindowAttributes lAttributes;
    XGetWindowAttributes(mDisplay->GetDisplay(),mX11ID,&lAttributes);
    mVisual=lAttributes.visual;
    mDepth=lAttributes.depth;
    mSize=CSize_d(lAttributes.width,lAttributes.height);
    mPos=CPoint_d(lAttributes.x,lAttributes.y);
}

CX11Window::~CX11Window()
{
    if(mX11ID) mAllX11Windows.erase(mX11ID);

    delete mWM;
}

////////////////////////// static members ////////////////////////

CX11Window *CX11Window::GetX11Window(Window pID,bool pForce)
{
    CX11Window *lWindow=mAllX11Windows[pID];

    if(lWindow)
        return lWindow;
    else if(pForce)
        return new CX11Window(pID,get_default_x11_display());
    else
        return nullptr;
}

//////////////////// event filter //////////////////////

void CX11Window::AddFilter(CX11EventFilter *pFilter)
{
    mFilters.push_back(pFilter);
}

void CX11Window::RemoveFilter(CX11EventFilter *pFilter)
{
    mFilters.erase(std::find(mFilters.begin(),mFilters.end(),pFilter));
}

//////////////////// window creation //////////////////

void CX11Window::Create(CPhysicalWindow *pParent,const CPoint_d &pPos,const CSize_d &pSize)
{
    msg_info() << "creating " << (void*)pParent << "\n";
    mParent=pParent?throwing_cast<CX11Window*>(pParent):get_x11_toolkit()->GetRootWindow();
    mPos=pPos;
    mSize=pSize;

    unsigned long lXAttrMask=CWWinGravity|CWEventMask;
    XSetWindowAttributes lXAttrs;
    
    bool lManage=GetWindow()&&ShouldManage(GetWindow().get());
    
    if(!lManage)
    {
        SetFlag(FLAG_OVERRIDE);
        lXAttrMask|=CWOverrideRedirect;
        lXAttrs.override_redirect=True;
    }

///\todo this based on something which is static
    mInputOnly=GetWindow()&&(GetWindow()->PaintMode()&CWindow::EPaintMode::INVISIBLE);

///\todo the events should be based also on what children request
    lXAttrs.event_mask=GetXEvents(GetWindow()?GetWindow()->Events():EVENT_DEFAULT);

    lXAttrs.win_gravity=NorthWestGravity;
//  lXAttrs.win_gravity=StaticGravity;

    int lClass;

    if(!mInputOnly)
    {
        lXAttrMask|=CWBitGravity;

//      lXAttrs.bit_gravity=StaticGravity;
        lXAttrs.bit_gravity=ForgetGravity;

        if(!lManage)
        {
            lXAttrMask|=CWSaveUnder;
            lXAttrs.save_under=true;
        }

        ///\todo
        if(0)//pAttribs&CWindow::PAINT_TRANSPARENT)
        {
            XVisualInfo lTemplate;
            lTemplate.screen=mDisplay->GetScreenNumber();
            lTemplate.depth=32;
            lTemplate.c_class=TrueColor;
            int lCount;
            XVisualInfo *lMatches=XGetVisualInfo(mDisplay->GetDisplay(),
                        VisualScreenMask|VisualDepthMask|VisualClassMask,
                        &lTemplate,&lCount);

            mVisual=nullptr;
            for(int i=0;i<lCount;i++)
            {
                msg_info() << "trying " << (lMatches+i)->visualid << "\n";
                int lValue;
                glXGetConfig(mDisplay->GetDisplay(),lMatches+i,GLX_USE_GL,&lValue);
                if(!lValue) continue;
                glXGetConfig(mDisplay->GetDisplay(),lMatches+i,GLX_DOUBLEBUFFER,&lValue);
                if(!lValue) continue;
                glXGetConfig(mDisplay->GetDisplay(),lMatches+i,GLX_ALPHA_SIZE,&lValue);
                if(lValue!=8) continue;
                glXGetConfig(mDisplay->GetDisplay(),lMatches+i,GLX_SAMPLES,&lValue);
                if(lValue!=0) continue;
                
                mVisual=lMatches[i].visual;
                break;
            }
            if(!mVisual)
            {
                throw XNotFound("can't find visual for ARGB window");
            }

            mDepth=32;
        }

        if(mVisual)
        {
        ///\todo
            lXAttrs.colormap=XCreateColormap(mDisplay->GetDisplay(),
                                             mDisplay->GetDefaultRoot(),
                                (Visual*)mVisual,AllocNone);
            lXAttrs.background_pixel=0;
            lXAttrs.border_pixel=0;
            lXAttrMask|=CWColormap|CWBackPixel|CWBorderPixel;
        }
        else
        {
            mVisual=mParent->XVisual();
            mDepth=mParent->Depth();
        }
        lClass=InputOutput;
    }
    else
    {
        lClass=InputOnly;
    }

    mX11ID=XCreateWindow(mDisplay->GetDisplay(),mParent->GetX11ID(),
            (int)mPos.x,(int)mPos.y,(int)mSize.x,(int)mSize.y,0,
            mDepth,lClass,mVisual,lXAttrMask,&lXAttrs);
    mAllX11Windows[mX11ID]=this;

    if(!XGetWindowAttributes(mDisplay->GetDisplay(), mX11ID, &mXAttrs))
    {
        throw XInternal("can't get attributes for newly created window");
    }

    if(!mInputOnly)
    {
        XSetWindowBackgroundPixmap(mDisplay->GetDisplay(),mX11ID,None);
    }

///\todo see what this is all about
//    if((pAttribs&CWindow::WM_NOMANAGING)&&GetWindow())
//    {
//        icccm::write_net_wm_window_type(mX11ID,GetWindow()->WindowType());
//    }

    mVisual=mXAttrs.visual;
    mDepth=mXAttrs.depth;

    if(GetWindow()&&IsWindowManaged())
        SetWMInfo();
}
///\todo see where all of this goes
#if 0
void CX11Window::AssignWindow(CWindow *pWindow,EEvents pEvents)
{
    mWindow=pWindow;
    Window lRoot,lParent,*lChildren;
    unsigned int lChildrenCount;

    if(!XQueryTree(mDisplay->GetDisplay(),mX11ID,&lRoot,&lParent,
                        &lChildren,&lChildrenCount))
    {
        throw XNotFound("can't find window in CX11Window::AssignWindow");
    }

    mAllX11Windows[mX11ID]=this;

    if(lParent)
    {
        CWindow *lW=GetWindow(lParent);
        if(lW)
        {
            mWindow->SetParent(lW);
            mParent=throwing_cast<CX11Window*>(lW->GetPhysical());
        }
    }

    if(lChildrenCount)
    {
        for(unsigned int i=0;i<lChildrenCount;i++)
        {
            CWindow *lW=GetWindow(lChildren[i]);
            if(lW)
            {
                mWindow->GetChildrenList().push_back(lW);
            }
        }
        XFree(lChildren);
    }

    if(!XGetWindowAttributes(mDisplay->GetDisplay(), mX11ID, &mXAttrs))
    {
        throw XNotFound("can't get window attributes in CX11Window::AssignWindow");
    }

    mVisual=mXAttrs.visual;
    mDepth=mXAttrs.depth;

    mPos=CPoint_d(mXAttrs.x,mXAttrs.y);
    mSize=CSize_d(mXAttrs.width,mXAttrs.height);

    mWindow->OnPhysicalPosChanged(mPos);
    mWindow->OnPhysicalSizeChanged(mSize);

    if(mXAttrs.map_state==IsViewable)
    {
        mWindow->OnPhysicalMap(true);
    }

    mInitialEventMask=mXAttrs.your_event_mask;

    SelectInput(pEvents);
}
#endif
void CX11Window::Destroy()
{
    XDestroyWindow(mDisplay->GetDisplay(),mX11ID);
    mAllX11Windows.erase(mX11ID);
    mX11ID=0;
}

img::CGC *CX11Window::GetGC(img::EGCCaps pCaps)
{
    return new gl::COpenGLGC(new CGLXContext(this));
}

CSize_i CX11Window::SurfaceSize()
{
    return CSize_i(mSize);
}

//////////////////// direct protocol access //////////////////

void CX11Window::ClearArea(const CRect_i &pR,bool pSend)
{
    XClearArea(mDisplay->GetDisplay(),mX11ID,pR.l,pR.t,pR.w(),pR.h(),pSend);
}

void CX11Window::Map(bool pRaised)
{
    if(pRaised)
        XMapRaised(mDisplay->GetDisplay(),mX11ID);
    else
        XMapWindow(mDisplay->GetDisplay(),mX11ID);

    if(GetFlag(FLAG_FOREIGN)&&GetWindow())
    {
        window_manager().OnPhysicalMap(this,true);
    }

    if(IsWindowManaged()&&mWM)
        mWM->mMapped=true;
}

void CX11Window::Unmap()
{
    XUnmapWindow(mDisplay->GetDisplay(),mX11ID);

    if(GetFlag(FLAG_FOREIGN)&&GetWindow())
    {
        window_manager().OnPhysicalMap(this,true);
    }

    if(IsWindowManaged())
    {
        XEvent lXE;
        lXE.xunmap.type=UnmapNotify;
        lXE.xunmap.send_event=True;
        lXE.xunmap.display=mDisplay->GetDisplay();
        lXE.xunmap.event=DefaultRootWindow(mDisplay->GetDisplay());
        lXE.xunmap.window=mX11ID;
        lXE.xunmap.from_configure=False;

        XSendEvent(mDisplay->GetDisplay(),
                    DefaultRootWindow(mDisplay->GetDisplay()),
                    False,SubstructureRedirectMask|SubstructureNotifyMask,
                    &lXE);
        XFlush(mDisplay->GetDisplay());

        while(1)
        {
            long int *lData;
            long int **lPData=&lData;
            int lReturn=mDisplay->GetWindowProperty(mX11ID,mDisplay->Atoms().mState,
                            mDisplay->Atoms().mState,(void**)lPData,2);
            if(lReturn<0)
            {
                break;
            }
            if(lData[0]==WithdrawnState)
            {
                XFree(lData);
                break;
            }
            sleep(chrono::milliseconds(1));
            XFree(lData);
        }

        mWM->mMapped=false;
    }
}

void CX11Window::Reparent(CPhysicalWindow *pParent,const CPoint_d &pPos)
{
    bool lRemap=false;

    if(IsWindowManaged()&&(!GetWindow()||GetWindow()->Mapped()))
    {
        Unmap();
        lRemap=true;
    }

    mParent=throwing_cast<CX11Window*>(pParent);

    XReparentWindow(mDisplay->GetDisplay(),mX11ID,mParent->GetX11ID(),
                        (int)pPos.x,(int)pPos.y);

    if(lRemap)
    {
        Map(false);
    }

    if(IsWindowManaged())
        SetWMInfo();
}

void CX11Window::AddToSaveSet()
{
    XAddToSaveSet(mDisplay->GetDisplay(),mX11ID);
}

void CX11Window::RemoveFromSaveSet()
{
    XRemoveFromSaveSet(mDisplay->GetDisplay(),mX11ID);
}

void CX11Window::Lower()
{
    XLowerWindow(mDisplay->GetDisplay(),mX11ID);
}

void CX11Window::Raise(const std::vector<CPhysicalWindow*> &pExc)
{
    if(!pExc.size())
    {
        XRaiseWindow(mDisplay->GetDisplay(),mX11ID);
        return;
    }

    //We don't really raise it... it must be below exceptions
    int lCount=pExc.size();
    Window lWindows[lCount+1];
    for(int i=0;i<lCount;i++)
    {
        lWindows[i]=throwing_cast<CX11Window*>(pExc[i])->GetX11ID();
    }
    lWindows[lCount]=mX11ID;

    XRaiseWindow(mDisplay->GetDisplay(),lWindows[0]);
    XRestackWindows(mDisplay->GetDisplay(),lWindows,lCount+1);
}

void CX11Window::StayOnTop(CPhysicalWindow *pWindow)
{
    XSetTransientForHint(mDisplay->GetDisplay(),mX11ID,
                pWindow?dynamic_cast<CX11Window*>(pWindow)->GetX11ID():None);
}

void CX11Window::SelectInput(EEvents pEvents)
{
    XSelectInput(mDisplay->GetDisplay(),mX11ID,
                GetXEvents(pEvents)|mInitialEventMask);
    XShapeSelectInput(mDisplay->GetDisplay(),mX11ID,
                    GetXShapeEvents(pEvents));
}

static int sX11Gravity[]=
{
    NorthGravity,
    SouthGravity,
    EastGravity,
    WestGravity,
    NorthWestGravity,
    SouthWestGravity,
    NorthEastGravity,
    SouthEastGravity,
    StaticGravity,
    ForgetGravity
};

void CX11Window::SetGravity(int pGravity)
{
    XSetWindowAttributes lXAttrs;
    lXAttrs.win_gravity=sX11Gravity[pGravity];
    XChangeWindowAttributes(mDisplay->GetDisplay(),
                    mX11ID,CWWinGravity,&lXAttrs);
}

void CX11Window::SetBackingStore(bool pBackingStore)
{
    XSetWindowAttributes lXAttrs;
    lXAttrs.backing_store=pBackingStore?Always:NotUseful;
    XChangeWindowAttributes(mDisplay->GetDisplay(),
                    mX11ID,CWBackingStore,&lXAttrs);
}

void CX11Window::SetNoShape(int pType)
{
    UnsetFlag(FLAG_SHAPED);

    if(pType&SHAPE_BOUND)
    {
        XShapeCombineMask(mDisplay->GetDisplay(),
                mX11ID,ShapeBounding,0,0,None,ShapeSet);
    }
    if(pType&SHAPE_CLIP)
    {
        XShapeCombineMask(mDisplay->GetDisplay(),
                mX11ID,ShapeClip,0,0,None,ShapeSet);
    }
    if(pType&SHAPE_INPUT)
    {
        XShapeCombineMask(mDisplay->GetDisplay(),
                mX11ID,ShapeInput,0,0,None,ShapeSet);
    }
}

void CX11Window::SetShape(const CRegion&,int /*pType*/,const CPoint_d&)
{
///\todo
//    if(pType&SHAPE_BOUND)
//    {
//        XShapeCombineRegion(mDisplay->GetDisplay(),mX11ID,ShapeBounding,
//                (int)pPoint.x,(int)pPoint.y,pRegion.GetID(),ShapeSet);
//    }
//    if(pType&SHAPE_CLIP)
//    {
//        XShapeCombineRegion(mDisplay->GetDisplay(),mX11ID,ShapeClip,
//                (int)pPoint.x,(int)pPoint.y,pRegion.GetID(),ShapeSet);
//    }
//    if(pType&SHAPE_INPUT)
//    {
//        XShapeCombineRegion(mDisplay->GetDisplay(),mX11ID,ShapeInput,
//                (int)pPoint.x,(int)pPoint.y,pRegion.GetID(),ShapeSet);
//    }
}

void CX11Window::SetShapeFromMask(CSurface *pSurface,int pType,
                                    const CPoint_d &pPoint)
{
    SetShapeFromFixesRegion(CFixesRegion((CPixmap*)pSurface),pType,pPoint);
}

void CX11Window::SetShapeFromRect(const CRect_d &pRect,int pType,
                                    const CPoint_d &pPoint)
{
    SetShapeFromFixesRegion(CFixesRegion(pRect),pType,pPoint);
}

void CX11Window::SetShapeFromWindow(CPhysicalWindow*,int /*pType*/,const CPoint_d&)
{
///\todo
//    SetShapeFromFixesRegion(CFixesRegion(pWindow),pType,pPoint);
}

void CX11Window::SetShapeFromFixesRegion(const CFixesRegion &pRegion,int pType,
                        const CPoint_d &pPoint)
{
    SetFlag(FLAG_SHAPED);

    if(!pRegion.GetID())
        SetNoShape(pType);

    if(pType&SHAPE_BOUND)
    {
        XFixesSetWindowShapeRegion(mDisplay->GetDisplay(),mX11ID,
            ShapeBounding,(int)pPoint.x,(int)pPoint.y,pRegion.GetID());
    }
    if(pType&SHAPE_CLIP)
    {
        XFixesSetWindowShapeRegion(mDisplay->GetDisplay(),mX11ID,
            ShapeClip,(int)pPoint.x,(int)pPoint.y,pRegion.GetID());
    }
    if(pType&SHAPE_INPUT)
    {
        XFixesSetWindowShapeRegion(mDisplay->GetDisplay(),mX11ID,
            ShapeInput,(int)pPoint.x,(int)pPoint.y,pRegion.GetID());
    }
}

void CX11Window::SetFocus()
{
    if(IsTopLevel()&&IsManaged())
    {
        mWM->Activate();
    }
    else
    {
        XSetInputFocus(mDisplay->GetDisplay(),mX11ID,RevertToNone,CurrentTime);

        if(GetFlag(FLAG_EMBEDDED))
        {
            SendXEmbedMessage(XEMBED_WINDOW_ACTIVATE);
            SendXEmbedMessage(XEMBED_FOCUS_IN,XEMBED_FOCUS_FIRST);
        }
        Window lReturn;
        int lDummy;
        XGetInputFocus(mDisplay->GetDisplay(),&lReturn,&lDummy);
    }
}

void CX11Window::SetPointer(PPointer pCursor)
{
    if(mCursor==pCursor) return;
    mCursor=pCursor;
    if(pCursor)
    {
        XDefineCursor(mDisplay->GetDisplay(),mX11ID,pCursor->GetID());
    }
    else
    {
        XUndefineCursor(mDisplay->GetDisplay(),mX11ID);
    }
}

void CX11Window::UpdateState()
{
    mWM->NETSetState();
    if(GetWindow())
        icccm::write_net_wm_window_type(mX11ID,GetWindow()->WindowType());
}

//////////////////// X embed protocol //////////////////

void CX11Window::SendXEmbedMessage(int pOpcode,int pDetail,int pData1,
                            int pData2)
{
    SendClientMessage(mDisplay->Atoms().mXEmbed,get_x11_toolkit()->GetTimeStamp(),
            pOpcode,pDetail,pData1,pData2);
}

void CX11Window::Embed(CPhysicalWindow *pChild)
{
    ///\todo reimplement this if we need it
//     CX11Window *lChild=throwing_cast<CX11Window*>(pChild);
// 
//     if(!lChild->GetFlag(FLAG_FOREIGN))
//     {
//         lChild->Reparent(GetWindow());
//         return pChild;
//     }
// 
// 
//     SXEmbedInfo *lEmbedInfo;
//     SXEmbedInfo **lPEmbedInfo=&lEmbedInfo;
// 
//     int lCount=mDisplay->GetWindowProperty(lChild->mX11ID,mDisplay->Atoms().mXEmbedInfo,
//                     mDisplay->Atoms().mXEmbedInfo,(void**)lPEmbedInfo,2);
//     if(lCount<2)
//         throw XInvalid(I18N("Embedding candidate doesn't have _XEMBED_INFO"));
// 
//     SetFlag(FLAG_EMBEDDER);
//     mXEmbeddee=lChild;
//     lChild->SetFlag(FLAG_EMBEDDED);
//     lChild->Unmap();
//     pChild->SetParent(GetWindow());
//     lChild->RemoveFromSaveSet();
// 
//     //Start the protocol itself
//     lChild->SendXEmbedMessage(XEMBED_EMBEDDED_NOTIFY,0,mX11ID,0);
// 
//     //update state
//     if(GetWindow()&&GetWindow()->IsActive())
//     {
//         lChild->SendClientMessage(XEMBED_WINDOW_ACTIVATE);
//     }
// 
//     if(lEmbedInfo->mFlags&0x01)
//     {
//         pChild->Show();
//     }
//     XFree(lEmbedInfo);
// 
//     return pChild;
}

void CX11Window::SetEmbedInfo()
{
    SXEmbedInfo lInfo={0,0};
    mDisplay->SetWindowProperty(mX11ID,mDisplay->Atoms().mXEmbedInfo,
                mDisplay->Atoms().mXEmbedInfo,32,2,&lInfo);
}

void CX11Window::Embed(TID pID)
{
//     CWindow *lChild=GetWindow(pID,true);
//     if(!lChild)
//         throw XNotFound(I18N("Can't find window to embed"));
//     return Embed(lChild);
}

//////////////////// grabbing the pointer //////////////////////

void CX11Window::GrabButton(EMouseButton pButton,EKeyMod pMods,
                bool pConfine,CPointer *pCursor)
{
    XGrabButton(mDisplay->GetDisplay(),
            CX11Dispatcher::TranslateButtonToX(pButton),
                get_x11_toolkit()->GetXMods(pMods),mX11ID,false,
            ButtonPressMask|PointerMotionMask|ButtonReleaseMask,
            GrabModeAsync,GrabModeAsync,pConfine?mX11ID:None,
            pCursor?pCursor->GetID():None);
}

void CX11Window::UngrabButton(EMouseButton pButton,EKeyMod pMods)
{
    XUngrabButton(mDisplay->GetDisplay(),
            CX11Dispatcher::TranslateButtonToX(pButton),
                  get_x11_toolkit()->GetXMods(pMods),mX11ID);
}

//////////////////// physical position & size ///////////////////

void CX11Window::Move(const CPoint_d &pPos)
{
    XMoveWindow(mDisplay->GetDisplay(),mX11ID,(int)pPos.x,(int)pPos.y);

    if(pPos==mPos) return;

    //this will change the position unless there is some override
    if(IsManaged()) //some override
    {
        SetFlag(FLAG_MOVEPENDING);

        if(IsWindowManaged())
        {
            mWM->InternalMove(pPos);
        }
    }
    else
    {
        mPos=pPos;
        if(GetWindow())
            window_manager().OnPhysicalGeometry(this,mPos,mSize);
    }
}

void CX11Window::Resize(const CSize_d &pSize)
{
    CSize_d lSize=pSize;

    if(lSize.x<1||lSize.y<1)
        lSize={1,1};

    XResizeWindow(mDisplay->GetDisplay(),mX11ID,(int)lSize.x,(int)lSize.y);

    if(lSize==mSize) return;

    if(IsManaged()) //some override
    {
        SetFlag(FLAG_RESIZEPENDING);

        if(IsWindowManaged())
        {
            mWM->InternalResize(pSize);
        }
    }
    else
    {
        mSize=lSize;
        if(GetWindow())
            window_manager().OnPhysicalGeometry(this,mPos,mSize);
    }
}

void CX11Window::MoveResize(const CPoint_d &pPos,const CSize_d &pSize)
{
    CSize_d lSize=pSize;

    if(lSize.x<1||lSize.y<1)
        lSize={1,1};

    XMoveResizeWindow(mDisplay->GetDisplay(),mX11ID,
                (int)pPos.x,(int)pPos.y,(int)lSize.x,(int)lSize.y);

    bool lReport=false;
    
    if(lSize!=mSize)
    {
        if(IsManaged())
        {
            SetFlag(FLAG_RESIZEPENDING);

            if(IsWindowManaged())
            {
                mWM->InternalResize(pSize);
            }
        }
        else
        {
            mSize=lSize;
            if(GetWindow())
                lReport=true;
        }
    }

    if(pPos!=mPos)
    {
        if(IsManaged()) //some override
        {
            SetFlag(FLAG_MOVEPENDING);

            if(IsWindowManaged())
            {
                mWM->InternalMove(pPos);
            }
        }
        else
        {
            mPos=pPos;
            if(GetWindow())
                lReport=true;
        }
    }

    if(lReport)
        window_manager().OnPhysicalGeometry(this,mPos,mSize);
}

void CX11Window::DispatchAll()
{
    get_x11_toolkit()->mDispatcher->DispatchPending(this,ConfigureNotify);
}

CPoint_d CX11Window::TranslateCoords(const CPoint_d &pPoint,CPhysicalWindow *pOther)
{
    int lReturnX,lReturnY;
    Window lDummy;

    XTranslateCoordinates(mDisplay->GetDisplay(),mX11ID,throwing_cast<CX11Window*>(pOther)->GetX11ID(),
                    (int)pPoint.x,(int)pPoint.y,&lReturnX,&lReturnY,&lDummy);

    return CPoint_d(lReturnX,lReturnY);
}

////////////////////// window-managed /////////////////////

void CX11Window::Freeze(bool pFrozen)
{
    if(IsWindowManaged())
    {
        if(pFrozen)
            SetFlag(FLAG_DONTFOCUS);
        else
            UnsetFlag(FLAG_DONTFOCUS);

        UpdateHints();
    }
}

EWindowState CX11Window::GetState()
{
    if(IsWindowManaged())
        return mWM->mState;
    else
        return EWindowState::NONE;
}

void CX11Window::Iconify(bool pB)
{
    if(IsWindowManaged())
        mWM->Iconify(pB);
}

void CX11Window::Maximize(EWindowState pMax)
{
    if(IsWindowManaged())
        mWM->Maximize(pMax);
}

void CX11Window::Shade(bool pB)
{
    if(IsWindowManaged())
        mWM->Shade(pB);
}

void CX11Window::Sticky(bool pB)
{
    if(IsWindowManaged())
        mWM->Sticky(pB);
}

void CX11Window::SetDesktop(int pDesktop)
{
    if(IsWindowManaged())
        mWM->SetDesktop(pDesktop);
}

void CX11Window::FullScreen(bool pB)
{
    if(IsWindowManaged())
        mWM->FullScreen(pB);
}

void CX11Window::StartMoveOperation(CMouseEvent *pE)
{
    if(IsWindowManaged())
        mWM->StartMoveOperation(pE);
}

void CX11Window::StartResizeOperation(CMouseEvent *pE,EDragState pType)
{
    if(IsWindowManaged())
        mWM->StartResizeOperation(pE,pType);
}

void CX11Window::SetStrut(const CRect_d &pRect)
{
    if(IsWindowManaged())
        mWM->SetStrut(pRect);
}

void CX11Window::SetOpacity(double pOpacity)
{
    if(IsWindowManaged())
        mWM->SetOpacity(pOpacity);
    else if(IsTopLevel())
    {
        if(pOpacity<0.0) pOpacity=0;
        if(pOpacity>1.0) pOpacity=1;
        SetWMProperty(mDisplay->Atoms().mNetWMWindowOpacity,(unsigned int)(pOpacity*0xffffffff));
    }
}

void CX11Window::SetTitle(const std::wstring &pTitle)
{
    if(IsWindowManaged())
        mWM->SetTitle(pTitle);
}

void CX11Window::SetIcon(PIcon pIcon)
{
    if(IsWindowManaged())
        mWM->SetIcon(pIcon);
}

void CX11Window::UpdateHints()
{
    mWM->ICCCMSetSizeHints();
}

//////////////////// inter-client communication //////////////////

void CX11Window::SendSyntheticExposeEvent(const CRect_d &pRect)
{
    XEvent lXE;
    lXE.xexpose.type=Expose;
    lXE.xexpose.send_event=true;
    lXE.xexpose.display=mDisplay->GetDisplay();
    lXE.xexpose.window=mX11ID;
    lXE.xexpose.x=(int)pRect.l;
    lXE.xexpose.y=(int)pRect.t;
    lXE.xexpose.width=(int)pRect.w();
    lXE.xexpose.height=(int)pRect.h();
    lXE.xexpose.count=0;

    XSendEvent(mDisplay->GetDisplay(),mX11ID,False,ExposureMask,&lXE);
}

void CX11Window::SendSyncRequest()
{
    if(mWM->mSyncRequest)
        SendClientMessage(mDisplay->Atoms().mNetWMSyncRequest);
}

void CX11Window::SendClientMessage(CX11Window *pDest,TID pAtom,int l1,int l2,
                            int l3,int l4,int l5,long pMask)
{
    if(pMask==-1) pMask=SubstructureNotifyMask|SubstructureRedirectMask;
    get_default_x11_display()->SendClientMessage(mX11ID,pDest->GetX11ID(),pAtom,l1,l2,l3,l4,l5,pMask);
}

void CX11Window::SendClientMessage(TID pAtom,int l1,int l2,int l3,int l4,int l5,
                        long pMask)
{
    if(pMask==-1) pMask=NoEventMask;
    get_default_x11_display()->SendClientMessage(mX11ID,mX11ID,pAtom,l1,l2,l3,l4,l5,pMask);
}

void CX11Window::OnSizeHintsChanged()
{
    mWM->UpdateInfo();
}

void CX11Window::OnActivateForeign(bool pActivated)
{
    if(pActivated)
    {
        XSetInputFocus(mDisplay->GetDisplay(),mX11ID,RevertToNone,CurrentTime);

        if(GetFlag(FLAG_EMBEDDED))
        {
            SendXEmbedMessage(XEMBED_WINDOW_ACTIVATE);
//          SendXEmbedMessage(XEMBED_FOCUS_IN,XEMBED_FOCUS_FIRST);
        }
    }
    else
    {
        if(GetFlag(FLAG_EMBEDDED))
        {
            SendXEmbedMessage(XEMBED_WINDOW_DEACTIVATE);
        }
    }
}

void CX11Window::SetWMInfo()
{
    if(!mWM)
    {
        mWM=new CX11WindowWM(this);
    }

    mWM->UpdateInfo();
}

bool CX11Window::GetWMProperty(Atom pAtom,std::wstring &pDest,Atom pType)
{
    if(!pType) pType=XA_STRING;

    return mDisplay->GetWindowProperty(mX11ID,pAtom,pType,pDest);
}

bool CX11Window::GetWMProperty(Atom pAtom,int *pDest)
{
    return mDisplay->GetWindowProperty(mX11ID,pAtom,pDest);
}

bool CX11Window::SetWMProperty(Atom pAtom,Atom *pList,int pCount)
{
    return mDisplay->SetWindowProperty(mX11ID,pAtom,XA_ATOM,32,pCount,
                    (unsigned char*)pList);
}

bool CX11Window::SetWMProperty(Atom pAtom,int *pList,int pCount)
{
    //HELP:weird, very weird:on 64 bits system format=32 actually means 64,
    //because it just means long int. movint to xcb??
    long int lList[pCount];
    for(int i=0;i<pCount;i++)
        lList[i]=pList[i];

    return mDisplay->SetWindowProperty(mX11ID,pAtom,XA_CARDINAL,32,pCount,
                    (unsigned char*)lList);
}

bool CX11Window::SetWMProperty(Atom pAtom,const std::wstring &pString,Atom pType)
{
    if(!pType) pType=XA_STRING;

    return mDisplay->SetWindowProperty(mX11ID,pAtom,pType,pString);
}

bool CX11Window::SetWMProperty(Atom pAtom,const std::wstring &pString,
                                const std::wstring &pString2,Atom pType)
{
    if(!pType) pType=XA_STRING;

    unsigned char lFinal[pString.length()+pString2.length()+2];

    int i;

    for(i=0;i<pString.length();i++)
    {
        lFinal[i]=pString[i];
    }
    lFinal[i++]='\0';

    for(int j=0;j<pString2.length();j++,i++)
    {
        lFinal[i]=pString[j];
    }
    lFinal[i++]='\0';

    return mDisplay->SetWindowProperty(mX11ID,pAtom,pType,8,i,lFinal);
}

bool CX11Window::SetWMProperty(Atom pAtom,const std::string &pString,Atom pType)
{
    if(!pType) pType=XA_STRING;

    return mDisplay->SetWindowProperty(mX11ID,pAtom,pType,utf32(pString));
}

bool CX11Window::SetWMProperty(Atom pAtom,const std::string &pString,
                                const std::string &pString2,Atom pType)
{
    if(!pType) pType=XA_STRING;

    unsigned char lFinal[pString.length()+pString2.length()+2];

    int i;

    for(i=0;i<pString.length();i++)
    {
        lFinal[i]=pString[i];
    }
    lFinal[i++]='\0';

    for(int j=0;j<pString2.length();j++,i++)
    {
        lFinal[i]=pString[j];
    }
    lFinal[i++]='\0';

    return mDisplay->SetWindowProperty(mX11ID,pAtom,pType,8,i,lFinal);
}

bool CX11Window::SetWMProperty(Atom pAtom,int pInt)
{
    return mDisplay->SetWindowProperty(mX11ID,pAtom,XA_CARDINAL,32,1,&pInt);
}

bool CX11Window::SetWMPropertyAsAtom(Atom pAtom,Atom pValue)
{
    return mDisplay->SetWindowPropertyAsAtom(mX11ID,pAtom,pValue);
}

void CX11Window::RemoveWMProperty(Atom pAtom)
{
    XDeleteProperty(mDisplay->GetDisplay(),mX11ID,pAtom);
}

bool CX11Window::AcquireSelection(Atom pAtom)
{
    XSetSelectionOwner(mDisplay->GetDisplay(),pAtom,mX11ID,
                        CurrentTime);
    TID lID=XGetSelectionOwner(mDisplay->GetDisplay(),pAtom);

    if(lID!=mX11ID)
    {
        msg_warn() << "Owning selection " << mDisplay->GetAtomName(pAtom) << " failed\n";
        return false;
    }
    return true;
}

void CX11Window::ReleaseSelection(Atom pAtom)
{
    XSetSelectionOwner(mDisplay->GetDisplay(),pAtom,0,CurrentTime);
}

void CX11Window::SetDesiredVisual(Visual *pVisual,int pDepth)
{
    mVisual=pVisual;
    mDepth=pDepth;
}

CGLXInterface *CX11Window::GLXInterface()
{
    if(!mGLXInterface)
        mGLXInterface.reset(new CGLXInterface(this));
    
    return mGLXInterface.get();
}

int CX11Window::GetXShapeOp(int pOp)
{
    if(pOp==SHAPE_SET)
        return ShapeSet;
    if(pOp==SHAPE_UNION)
        return ShapeUnion;
    if(pOp==SHAPE_INTERSECTION)
        return ShapeIntersect;
    if(pOp==SHAPE_INVERT)
        return ShapeInvert;
    if(pOp==SHAPE_SUBTRACT)
        return ShapeSubtract;
    throw XArgument(I18N("Wrong window shape op"));
}

long CX11Window::GetXEvents(EEvents pE)
{
    long lE=FocusChangeMask;

    if((pE&EVENT_KEYDOWN)||(pE&EVENT_CHAR))
        lE|=KeyPressMask;
    if(pE&EVENT_KEYUP)
        lE|=KeyReleaseMask;
    if(pE&EVENT_BUTTONDOWN)
        lE|=ButtonPressMask;
    if(pE&EVENT_BUTTONUP)
        lE|=ButtonReleaseMask;
    if(pE&EVENT_BUTTONMOVE)
        lE|=ButtonMotionMask;
    if(pE&EVENT_MOUSEMOVE)
        lE|=PointerMotionMask;
    if(pE&EVENT_MOUSEENTER)
        lE|=EnterWindowMask|LeaveWindowMask;
    if((pE&EVENT_MOVE)||(pE&EVENT_SIZE)||!IsForeign()) //Our own windows
        lE|=StructureNotifyMask;
    if(pE&EVENT_EXPOSE)
        lE|=ExposureMask;
    if(pE&EVENT_CHILDGEOMETRY)
        lE|=SubstructureNotifyMask;
    if(pE&EVENT_CHILDREQUESTS)
        lE|=SubstructureRedirectMask;
    if(pE&EVENT_COLORMAP)
        lE|=ColormapChangeMask;
    if(pE&EVENT_PROPERTY)
        lE|=PropertyChangeMask;

    if(mInputOnly)
    {
        lE&=~(ExposureMask|ColormapChangeMask);
    }

    return lE;
}

unsigned long CX11Window::GetXShapeEvents(EEvents pE)
{
    unsigned long lE=None;

    if(pE&EVENT_SHAPE)
        lE|=ShapeNotifyMask;

    return lE;
}

void CX11Window::OnXEmbedRequestFocus()
{
    if(!GetFlag(FLAG_EMBEDDER)) return;

    mXEmbeddee->GetWindow()->SetFocus(false);
    mXEmbeddee->SetFocus();
    mXEmbeddee->SendXEmbedMessage(XEMBED_WINDOW_ACTIVATE);
    mXEmbeddee->SendXEmbedMessage(XEMBED_FOCUS_IN,XEMBED_FOCUS_CURRENT);
}

void CX11Window::BitBlt(const CPoint_d &pPoint,const CSize_d &pSize,
                        CSurface *pSrc,const CPoint_d &pSrcOrigin,
                        bool /*pGraphicsExposures*/)
{
//     CX11Picture::Composite(*(INTERFACE_RENDERTILED(pSrc)),CX11Picture::mNull,
//         *(INTERFACE_RENDER(this)),pSrcOrigin,pSrcOrigin,pPoint,pSize,img::ECompOp::SRC);

/*
    XGCValues lGCValues;
    GC lGC=XCreateGC(mDisplay->GetDisplay(),mX11ID,None,&lGCValues);

    XCopyArea(mDisplay->GetDisplay(),INTERFACE_X11(pSrc)->mX11ID,mX11ID,lGC,
            (int)pSrcOrigin.x,(int)pSrcOrigin.y,(int)pSize.x,(int)pSize.y,
            (int)pPoint.x,(int)pPoint.y);

    XFreeGC(mDisplay->GetDisplay(),lGC);
*/
}

CExposeHelper *CX11Window::GetExposeHelper()
{
    if(!mExposeHelper) mExposeHelper.reset(new CGLXExposeHelper(this));

    return mExposeHelper.get();
}

bool CX11Window::ShouldManage(CWindow *pWindow)
{
    switch(pWindow->WindowType())
    {
    case EWindowType::POPUPMENU:
    case EWindowType::TOOLTIP:
    case EWindowType::NOTIFICATION:
    case EWindowType::COMBO:
    case EWindowType::DND:
        return false;
    default:
        return true;
    }
}

/*namespace x11*/ } /*namespace gui*/ } /*namespace buola*/ }
